Skip to content

Conversation

pcc
Copy link
Contributor

@pcc pcc commented Aug 27, 2025

The prefalign attribute determines the function's preferred alignment.
By default, the function's preferred alignment is set in a target-specific
way, but it may be overridden with this attribute.

The backend logic will be added in followup patches.

Part of this RFC:
https://discourse.llvm.org/t/rfc-enhancing-function-alignment-attributes/88019

Created using spr 1.3.6-beta.1
@llvmbot
Copy link
Member

llvmbot commented Aug 27, 2025

@llvm/pr-subscribers-llvm-ir

Author: Peter Collingbourne (pcc)

Changes

The prefalign attribute determines the function's preferred alignment.
By default, the function's preferred alignment is set in a target-specific
way, but it may be overridden with this attribute.

The backend logic will be added in followup patches.

Part of this RFC:
https://discourse.llvm.org/t/rfc-enhancing-function-alignment-attributes/88019


Full diff: https://github.com/llvm/llvm-project/pull/155527.diff

10 Files Affected:

  • (modified) llvm/docs/LangRef.rst (+23-8)
  • (modified) llvm/include/llvm/AsmParser/LLParser.h (+1-1)
  • (modified) llvm/include/llvm/AsmParser/LLToken.h (+1)
  • (modified) llvm/include/llvm/IR/Function.h (+8-1)
  • (modified) llvm/lib/AsmParser/LLLexer.cpp (+1)
  • (modified) llvm/lib/AsmParser/LLParser.cpp (+13-10)
  • (modified) llvm/lib/Bitcode/Reader/BitcodeReader.cpp (+7)
  • (modified) llvm/lib/Bitcode/Writer/BitcodeWriter.cpp (+3-1)
  • (modified) llvm/lib/IR/AsmWriter.cpp (+2)
  • (modified) llvm/test/Bitcode/compatibility.ll (+14)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index b122fd352eaf5..80f9b7c150b67 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -877,7 +877,9 @@ an optional ``unnamed_addr`` attribute, a return type, an optional
 name, a (possibly empty) argument list (each with optional :ref:`parameter
 attributes <paramattrs>`), optional :ref:`function attributes <fnattrs>`,
 an optional address space, an optional section, an optional partition,
-an optional alignment, an optional :ref:`comdat <langref_comdats>`,
+an optional minimum alignment,
+an optional preferred alignment,
+an optional :ref:`comdat <langref_comdats>`,
 an optional :ref:`garbage collector name <gc>`, an optional :ref:`prefix <prefixdata>`,
 an optional :ref:`prologue <prologuedata>`,
 an optional :ref:`personality <personalityfn>`,
@@ -891,8 +893,8 @@ Syntax::
            <ResultType> @<FunctionName> ([argument list])
            [(unnamed_addr|local_unnamed_addr)] [AddrSpace] [fn Attrs]
            [section "name"] [partition "name"] [comdat [($name)]] [align N]
-           [gc] [prefix Constant] [prologue Constant] [personality Constant]
-           (!name !N)* { ... }
+           [prefalign N] [gc] [prefix Constant] [prologue Constant]
+           [personality Constant] (!name !N)* { ... }
 
 The argument list is a comma-separated sequence of arguments where each
 argument is of the following form:
@@ -942,11 +944,24 @@ LLVM allows an explicit section to be specified for functions. If the
 target supports it, it will emit functions to the section specified.
 Additionally, the function can be placed in a COMDAT.
 
-An explicit alignment may be specified for a function. If not present,
-or if the alignment is set to zero, the alignment of the function is set
-by the target to whatever it feels convenient. If an explicit alignment
-is specified, the function is forced to have at least that much
-alignment. All alignments must be a power of 2.
+An explicit minimum alignment (``align``) may be specified for a
+function. If not present, or if the alignment is set to zero, the
+alignment of the function is set according to the preferred alignment
+rules described below. If an explicit minimum alignment is specified, the
+function is forced to have at least that much alignment. All alignments
+must be a power of 2.
+
+An explicit preferred alignment (``prefalign``) may also be specified
+for a function (definitions only, and must be a power of 2). If a
+function does not have a preferred alignment attribute, the preferred
+alignment is determined in a target-specific way. The final alignment
+of the function is determined in the following way: if the function
+size is less than the minimum alignment, the function's alignment will
+be at least the minimum alignment. Otherwise, if the function size is
+between the minimum alignment and the preferred alignment, the function's
+alignment will be at least the power of 2 greater than or equal to the
+function size. Otherwise, the function's alignment will be at least the
+preferred alignment.
 
 If the ``unnamed_addr`` attribute is given, the address is known to not
 be significant and two identical functions can be merged.
diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index c01de4a289a69..5970b388b6bcd 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -311,7 +311,7 @@ namespace llvm {
                                  GlobalValueSummary::ImportKind &Res);
     void parseOptionalDLLStorageClass(unsigned &Res);
     bool parseOptionalCallingConv(unsigned &CC);
-    bool parseOptionalAlignment(MaybeAlign &Alignment,
+    bool parseOptionalAlignment(lltok::Kind KW, MaybeAlign &Alignment,
                                 bool AllowParens = false);
     bool parseOptionalCodeModel(CodeModel::Model &model);
     bool parseOptionalDerefAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes);
diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h
index e6a0eae9da30c..65d52747abc62 100644
--- a/llvm/include/llvm/AsmParser/LLToken.h
+++ b/llvm/include/llvm/AsmParser/LLToken.h
@@ -130,6 +130,7 @@ enum Kind {
   kw_prefix,
   kw_prologue,
   kw_c,
+  kw_prefalign,
 
   kw_cc,
   kw_ccc,
diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index d3497716ca844..0f9f526556847 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -85,7 +85,8 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
   unsigned BlockNumEpoch = 0;
 
   mutable Argument *Arguments = nullptr;  ///< The formal arguments
-  size_t NumArgs;
+  uint32_t NumArgs;
+  MaybeAlign PreferredAlign;
   std::unique_ptr<ValueSymbolTable>
       SymTab;                             ///< Symbol table of args/instructions
   AttributeList AttributeSets;            ///< Parameter attributes
@@ -1043,6 +1044,12 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
   /// defined.
   void setAlignment(MaybeAlign Align) { GlobalObject::setAlignment(Align); }
 
+  /// Returns the prefalign of the given function.
+  MaybeAlign getPreferredAlignment() const { return PreferredAlign; }
+
+  /// Sets the prefalign attribute of the Function.
+  void setPreferredAlignment(MaybeAlign Align) { PreferredAlign = Align; }
+
   /// Return the value for vscale based on the vscale_range attribute or 0 when
   /// unknown.
   unsigned getVScaleValue() const;
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 3d5bd6155536e..e51ab2c584770 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -625,6 +625,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(gc);
   KEYWORD(prefix);
   KEYWORD(prologue);
+  KEYWORD(prefalign);
 
   KEYWORD(no_sanitize_address);
   KEYWORD(no_sanitize_hwaddress);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 1bc2906f63b07..415a7b99a3c80 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1449,7 +1449,7 @@ bool LLParser::parseGlobal(const std::string &Name, unsigned NameID,
         return true;
     } else if (Lex.getKind() == lltok::kw_align) {
       MaybeAlign Alignment;
-      if (parseOptionalAlignment(Alignment))
+      if (parseOptionalAlignment(lltok::kw_align, Alignment))
         return true;
       if (Alignment)
         GV->setAlignment(*Alignment);
@@ -1548,7 +1548,7 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
         return true;
       Alignment = Align(Value);
     } else {
-      if (parseOptionalAlignment(Alignment, true))
+      if (parseOptionalAlignment(lltok::kw_align, Alignment, true))
         return true;
     }
     B.addAlignmentAttr(Alignment);
@@ -2382,10 +2382,11 @@ bool LLParser::parseOptionalFunctionMetadata(Function &F) {
 
 /// parseOptionalAlignment
 ///   ::= /* empty */
-///   ::= 'align' 4
-bool LLParser::parseOptionalAlignment(MaybeAlign &Alignment, bool AllowParens) {
+///   ::= KW 4
+bool LLParser::parseOptionalAlignment(lltok::Kind KW, MaybeAlign &Alignment,
+                                      bool AllowParens) {
   Alignment = std::nullopt;
-  if (!EatIfPresent(lltok::kw_align))
+  if (!EatIfPresent(KW))
     return false;
   LocTy AlignLoc = Lex.getLoc();
   uint64_t Value = 0;
@@ -2695,7 +2696,7 @@ bool LLParser::parseOptionalCommaAlign(MaybeAlign &Alignment,
     if (Lex.getKind() != lltok::kw_align)
       return error(Lex.getLoc(), "expected metadata or 'align'");
 
-    if (parseOptionalAlignment(Alignment))
+    if (parseOptionalAlignment(lltok::kw_align, Alignment))
       return true;
   }
 
@@ -6705,7 +6706,7 @@ bool LLParser::parseFunctionHeader(Function *&Fn, bool IsDefine,
   LocTy BuiltinLoc;
   std::string Section;
   std::string Partition;
-  MaybeAlign Alignment;
+  MaybeAlign Alignment, PrefAlignment;
   std::string GC;
   GlobalValue::UnnamedAddr UnnamedAddr = GlobalValue::UnnamedAddr::None;
   unsigned AddrSpace = 0;
@@ -6722,7 +6723,8 @@ bool LLParser::parseFunctionHeader(Function *&Fn, bool IsDefine,
       (EatIfPresent(lltok::kw_section) && parseStringConstant(Section)) ||
       (EatIfPresent(lltok::kw_partition) && parseStringConstant(Partition)) ||
       parseOptionalComdat(FunctionName, C) ||
-      parseOptionalAlignment(Alignment) ||
+      parseOptionalAlignment(lltok::kw_align, Alignment) ||
+      parseOptionalAlignment(lltok::kw_prefalign, PrefAlignment) ||
       (EatIfPresent(lltok::kw_gc) && parseStringConstant(GC)) ||
       (EatIfPresent(lltok::kw_prefix) && parseGlobalTypeAndValue(Prefix)) ||
       (EatIfPresent(lltok::kw_prologue) && parseGlobalTypeAndValue(Prologue)) ||
@@ -6824,6 +6826,7 @@ bool LLParser::parseFunctionHeader(Function *&Fn, bool IsDefine,
   Fn->setUnnamedAddr(UnnamedAddr);
   if (Alignment)
     Fn->setAlignment(*Alignment);
+  Fn->setPreferredAlignment(PrefAlignment);
   Fn->setSection(Section);
   Fn->setPartition(Partition);
   Fn->setComdat(C);
@@ -8446,7 +8449,7 @@ int LLParser::parseAlloc(Instruction *&Inst, PerFunctionState &PFS) {
   bool AteExtraComma = false;
   if (EatIfPresent(lltok::comma)) {
     if (Lex.getKind() == lltok::kw_align) {
-      if (parseOptionalAlignment(Alignment))
+      if (parseOptionalAlignment(lltok::kw_align, Alignment))
         return true;
       if (parseOptionalCommaAddrSpace(AddrSpace, ASLoc, AteExtraComma))
         return true;
@@ -8461,7 +8464,7 @@ int LLParser::parseAlloc(Instruction *&Inst, PerFunctionState &PFS) {
         return true;
       if (EatIfPresent(lltok::comma)) {
         if (Lex.getKind() == lltok::kw_align) {
-          if (parseOptionalAlignment(Alignment))
+          if (parseOptionalAlignment(lltok::kw_align, Alignment))
             return true;
           if (parseOptionalCommaAddrSpace(AddrSpace, ASLoc, AteExtraComma))
             return true;
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 22a0d0ffdbaab..abdd58eeed612 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -4385,6 +4385,13 @@ Error BitcodeReader::parseFunctionRecord(ArrayRef<uint64_t> Record) {
     Func->setPartition(StringRef(Strtab.data() + Record[17], Record[18]));
   }
 
+  if (Record.size() > 19) {
+    MaybeAlign PrefAlignment;
+    if (Error Err = parseAlignmentValue(Record[19], PrefAlignment))
+      return Err;
+    Func->setPreferredAlignment(PrefAlignment);
+  }
+
   ValueList.push_back(Func, getVirtualTypeID(Func->getType(), FTyID));
 
   if (OperandInfo.PersonalityFn || OperandInfo.Prefix || OperandInfo.Prologue)
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index a1d5b36bde64d..edb125a214405 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1644,7 +1644,8 @@ void ModuleBitcodeWriter::writeModuleInfo() {
     // FUNCTION:  [strtab offset, strtab size, type, callingconv, isproto,
     //             linkage, paramattrs, alignment, section, visibility, gc,
     //             unnamed_addr, prologuedata, dllstorageclass, comdat,
-    //             prefixdata, personalityfn, DSO_Local, addrspace]
+    //             prefixdata, personalityfn, DSO_Local, addrspace,
+    //             partition_strtab, partition_size, prefalign]
     Vals.push_back(addToStrtab(F.getName()));
     Vals.push_back(F.getName().size());
     Vals.push_back(VE.getTypeID(F.getFunctionType()));
@@ -1671,6 +1672,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
     Vals.push_back(F.getAddressSpace());
     Vals.push_back(addToStrtab(F.getPartition()));
     Vals.push_back(F.getPartition().size());
+    Vals.push_back(getEncodedAlign(F.getPreferredAlignment()));
 
     unsigned AbbrevToUse = 0;
     Stream.EmitRecord(bitc::MODULE_CODE_FUNCTION, Vals, AbbrevToUse);
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index dc6d599fa9585..3d3d3ba2eee33 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -4229,6 +4229,8 @@ void AssemblyWriter::printFunction(const Function *F) {
   maybePrintComdat(Out, *F);
   if (MaybeAlign A = F->getAlign())
     Out << " align " << A->value();
+  if (MaybeAlign A = F->getPreferredAlignment())
+    Out << " prefalign " << A->value();
   if (F->hasGC())
     Out << " gc \"" << F->getGC() << '"';
   if (F->hasPrefixData()) {
diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll
index 0b5ce08c00a23..b02cf1feed04e 100644
--- a/llvm/test/Bitcode/compatibility.ll
+++ b/llvm/test/Bitcode/compatibility.ll
@@ -759,6 +759,20 @@ declare void @f.align4() align 4
 declare void @f.align8() align 8
 ; CHECK: declare void @f.align8() align 8
 
+; Functions -- prefalign
+define void @f.prefalign2() prefalign 2 {
+  ret void
+}
+; CHECK: define void @f.prefalign2() prefalign 2
+define void @f.prefalign4() prefalign 4 {
+  ret void
+}
+; CHECK: define void @f.prefalign4() prefalign 4
+define void @f.prefalign8() prefalign 8 {
+  ret void
+}
+; CHECK: define void @f.prefalign8() prefalign 8
+
 ; Functions -- GC
 declare void @f.gcshadow() gc "shadow-stack"
 ; CHECK: declare void @f.gcshadow() gc "shadow-stack"

@pcc pcc requested review from efriedma-quic and nikic August 27, 2025 00:16
between the minimum alignment and the preferred alignment, the function's
alignment will be at least the power of 2 greater than or equal to the
function size. Otherwise, the function's alignment will be at least the
preferred alignment.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems overly specific: if you read it literally, it means that we have to precisely compute the size of the function, which is probably not feasible in general, and that optimizations aren't allowed to modify prefalign. I think I'd prefer to say it's a hint, which can be overriden by PGO/CFI jump tables/etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted this to describe the lower bound on the alignment (because e.g. non-ELF or -fno-function-sections can result in a higher alignment), but yeah, it's probably best just to say that it's a hint; the actual rules are too complex to specify here. Done.

Created using spr 1.3.6-beta.1
@pcc
Copy link
Contributor Author

pcc commented Sep 12, 2025

Ping

pcc added 2 commits September 12, 2025 14:33
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
@pcc
Copy link
Contributor Author

pcc commented Oct 6, 2025

Ping

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants